package com.flypigs.lock;

import lombok.extern.slf4j.Slf4j;
import org.ehcache.Cache;
import org.ehcache.CacheManager;
import org.ehcache.config.CacheConfiguration;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.data.redis.core.RedisTemplate;
import redis.clients.jedis.JedisCluster;

import javax.annotation.PostConstruct;
import java.io.Serializable;
import java.util.Map;

/**
 * ehcache时：必须配置distributedLockCache，且key为string  value为EhcacheValue的cache
 */
@Slf4j
public class LockManager {
    static final String JEDIS_CLUSTER_CLASS = "redis.clients.jedis.JedisCluster";
    static final String REDIS_TEMPLATE_CLASS = "org.springframework.data.redis.core.RedisTemplate";
    static final String EHCACHE_CLASS = "org.ehcache.CacheManager";

    @Autowired
    ApplicationContext applicationContext;
    private Object dependObj;
    private Class cacheClass;
    private Cache cache;

    @PostConstruct
    private void init(){
        //检查是否存在jedis
        Class clazz = null;
        try {
            clazz = Class.forName(JEDIS_CLUSTER_CLASS);
            Map<String,Object> objs = applicationContext.getBeansOfType(clazz);
            if(objs != null && objs.size() > 0){
                dependObj = objs.values().toArray()[0];
                cacheClass = clazz;
            }
        } catch (Exception e) {
            log.warn(e.getClass().getName() + ":" + e.getMessage());
        }
        //检查是事存在redis
        if(dependObj == null) {
            try {
                clazz = Class.forName(REDIS_TEMPLATE_CLASS);
                Map<String,Object> objs = applicationContext.getBeansOfType(clazz);
                if(objs != null && objs.size() > 0){
                    dependObj = objs.values().toArray()[0];
                    cacheClass = clazz;
                }
            } catch (Exception e) {
                log.warn(e.getClass().getName() + ":" + e.getMessage());
            }
        }
        //检查是否存在ehcache
        if(dependObj == null) {
            try {
                clazz = Class.forName(EHCACHE_CLASS);
                Map<String,Object> objs = applicationContext.getBeansOfType(clazz);
                if(objs != null && objs.size() > 0){
                    dependObj = objs.values().toArray()[0];
                    cacheClass = clazz;

                    if(this.cache == null) {
                        if (this.cache == null) {
                            Map<String, CacheConfiguration<?, ?>> cfgs = ((CacheManager)dependObj).getRuntimeConfiguration().getCacheConfigurations();
                            //先查看是否存在名为distributedLockCache 且 key 为String 或Object 或Serializable类型； value为 EhcacheValue或Object或Serializable类型
                            //同时过期策略必须为 EhcacheValueExpiryPolicy
                            cfgs.keySet().forEach(name ->{
                                if(this.cache == null && name.equals("distributedLockCache")){
                                    CacheConfiguration cfg = cfgs.get(name);
                                    if( (cfg.getKeyType() == String.class || cfg.getKeyType() == Object.class || cfg.getKeyType() == Serializable.class)
                                            && (cfg.getKeyType() == EhcacheValue.class )
                                            && EhcacheValueExpiryPolicy.class.isAssignableFrom(cfg.getExpiryPolicy().getClass())){
                                        this.cache = ((CacheManager) dependObj).getCache(name,cfg.getKeyType(),cfg.getValueType());
                                    }
                                }
                            });
                            //查询无论cacheName是什么，只要且 key 为String 或Object 或Serializable类型； value为 EhcacheValue或Object或Serializable类型
                            if(this.cache == null){
                                cfgs.keySet().forEach(name ->{
                                    if(this.cache == null) {
                                        CacheConfiguration cfg = cfgs.get(name);
                                        if ((cfg.getKeyType() == String.class || cfg.getKeyType() == Object.class || cfg.getKeyType() == Serializable.class)
                                                && (cfg.getValueType() == EhcacheValue.class)
                                                && EhcacheValueExpiryPolicy.class.isAssignableFrom(cfg.getExpiryPolicy().getClass())){
                                            this.cache = ((CacheManager) dependObj).getCache(name, cfg.getKeyType(), cfg.getValueType());
                                        }
                                    }
                                });
                            }
                        }
                    }
                }
            } catch (Exception e) {
                log.warn(e.getClass().getName() + ":" + e.getMessage());
            }

            if(this.cache == null){
                throw new RuntimeException("找不到名为distributedLockCache的缓存或key为String/Object/Serializable value为EhcacheValue类型，过期策略为EhcacheValueExpiryPolicy 的缓存");
            }
        }
    }

    public DistributedLock getLock(){
        if(dependObj == null){
            return null;
        }

        if(cacheClass.getName().equals(JEDIS_CLUSTER_CLASS)){
            return new JedisClusterLock((JedisCluster) dependObj);
        }else if(cacheClass.getName().equals(REDIS_TEMPLATE_CLASS)){
            return new RedisTemplateLock((RedisTemplate) dependObj);
        }else if(cacheClass.getName().equals(EHCACHE_CLASS)){
            return new EhcacheLock((CacheManager) dependObj,this.cache);
        }
        return null;
    }


}
